package cn.xzqwjw.taskmanager.security.filter;

import cn.xzqwjw.taskmanager.security.TokenAdmin;
import cn.xzqwjw.taskmanager.utils.CommonUtil;
import cn.xzqwjw.taskmanager.utils.JsonUtil;
import cn.xzqwjw.taskmanager.utils.MyMapUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/*
 * 对于管理员登录行为，security 通过定义一个Filter来拦截 /api/admin/login 来实现的
 * spring security 默认支持form方式登录，所以对于使用 json 发送登录信息的情况
 * 我们自己定义一个 Filter ，这个 Filter 直接从 AbstractAuthenticationProcessingFilter 继承
 * 只需要实现两部分，一个是 RequestMatcher，指名拦截的 Request 类型
 * 另外一个就是从 json body 中提取出 username 和 password 提交给 AuthenticationManager
 */

/**
 * @author rush
 */
public class AuthLoginAdminFilter extends AbstractAuthenticationProcessingFilter {

  public AuthLoginAdminFilter() {
    // 在这里指定从哪个 url 获取 login 用的 username 和 password
    super(new AntPathRequestMatcher("/api/admin/login", "POST"));
  }

  @Override
  public void afterPropertiesSet() {
    CommonUtil.checkNull(getAuthenticationManager(), getSuccessHandler(), getFailureHandler());
  }

  /**
   * 捕获 request 中的 post 过来的 json 数据并封装成 token 后给 provider 鉴权
   */
  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException, IOException {
    String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
    if (!StringUtils.hasText(body)) {
      return null;
    }
    String username, password, validateCode, ipInner, ipOuter;
    boolean isRememberMe;
    TokenAdmin token;
    try {
      Map<?, ?> paramsMap = JsonUtil.json2Obj(body, Map.class);
      username = MyMapUtil.getValueStr(paramsMap, "Username", true);
      password = MyMapUtil.getValueStr(paramsMap, "Password", true);
      CommonUtil.checkNull(username, password);

      validateCode = MyMapUtil.getValueStr(paramsMap, "ValidateCode", false);
      isRememberMe = Boolean.TRUE.equals(paramsMap.get("IsRememberMe"));
      ipInner = MyMapUtil.getValueStr(paramsMap, "IpInner", false);
      ipOuter = MyMapUtil.getValueStr(paramsMap, "IpOuter", false);

      token = new TokenAdmin(
          username,
          password,
          validateCode,
          isRememberMe,
          ipInner,
          ipOuter);
    } catch (Exception ex) {
      throw new UsernameNotFoundException("参数错误");
    }
    return this.getAuthenticationManager().authenticate(token);
  }

}
